/*
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.

SPDX-License-Identifier: Apache-2.0
*/

package main

import (
	"crypto/rand"
	"math/big"
	"context"
	"fmt"

	"chainmaker.org/chainmaker/pb-go/v2/common"
	sdk "chainmaker.org/chainmaker/sdk-go/v2"

	// "github.com/360EntSecGroup-Skylar/excelize/v2"
	"log"

	"github.com/panjf2000/ants"

	// "strconv"
	"sync"
	"time"
)

const (
	createContractTimeout = 5
	claimVersion          = "1.0.0"

	claimContractName = "evidence"

	claimByteCodePath = "../testdata/claim-docker-demo/evidence.7z"

	sdkConfigPKUser1Path     = "../sdk_configs/sdk_config_pk_user1.yml"
	sdkConfigOrg1Client1Path = "../sdk_configs/sdk_config_org1_client1.yml"
	threadNum                = 1000 // 协程池大小 并发数量
	// loopNum					 = 1 //未生效
	testInvoke = false
	testQuery  = true
)

// EvidenceData 调用合约的结构体
type EvidenceData struct {
	Id       string
	Hash     string
	Metadata string
}

type GoPoolData struct {
	client        *sdk.ChainClient
	chainCodeData *EvidenceData
	wg            *sync.WaitGroup
	onlyHeader    bool
}

func main() {
	fmt.Println("---------1----------")
	var (
		// exist                             bool
		// client *sdk.ChainClient // 区块链sdk客户端
		err    error
		// invokePool *ants.PoolWithFunc // 并发协程池
	)
	count := 1000000
	// // 初始化客户端
	// fmt.Println("初始化客户端")
	// client, err = sdk.NewChainClient(
	// 	sdk.WithConfPath(sdkConfigPKUser1Path),
	// 	// sdk.WithConfPath(sdkConfigOrg1Client1Path),
	// )
	// if err != nil {
	// 	log.Fatalln("创建client失败, 错误:" + err.Error())
	// }
	// fmt.Println(client.GetCurrentBlockHeight())

	var wg sync.WaitGroup //这里使用等待是为了看出结果，阻塞主线程，防止直接停止
	// 创建一个goroutine池
	fmt.Println("--------------创建一个goroutine池")
	subPool, err := ants.NewPoolWithFunc(threadNum, subscribeChainBlock)
	if err != nil {
		log.Fatalln("创建协程池失败，错误：" + err.Error())
	}
	defer subPool.Release()
	for i := 0; i < count; i++ {
		wg.Add(1)
		goPoolData := GoPoolData{
			// client:     client,
			wg:         &wg,
			onlyHeader: false,
		}
		// 协程执行存证方法
		_ = subPool.Invoke(goPoolData)
	}
	// 阻塞所有协程执行完成
	wg.Wait()

}

// func subscribeChainBlock(client *sdk.ChainClient, onlyHeader bool){
func subscribeChainBlock(data interface{}) {
	goPoolData := data.(GoPoolData)
	// client := goPoolData.client
	wg := goPoolData.wg
	onlyHeader := goPoolData.onlyHeader
	defer wg.Done()
		// 初始化客户端
	fmt.Println("初始化客户端")
	client, err := sdk.NewChainClient(
		sdk.WithConfPath(sdkConfigPKUser1Path),
		// sdk.WithConfPath(sdkConfigOrg1Client1Path),
	)
	if err != nil {
		log.Fatalln("创建client失败, 错误:" + err.Error())
	}
	// fmt.Println(client.GetCurrentBlockHeight())

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	start_height, _ := rand.Int(rand.Reader, big.NewInt(4280))
	end_height := start_height.Int64()+10

	fmt.Println(start_height,end_height)

	c, err := client.SubscribeBlock(ctx, start_height.Int64(), end_height, true, onlyHeader)
	if err != nil {
		log.Fatalln(err)
		return
	}

	for {
		select {
		case block, ok := <-c:
			// fmt.Println("=============================")
			if !ok {
				fmt.Println("chan is close!")
				return
			}

			if block == nil {
				log.Fatalln("require not nil")
			}

			if onlyHeader {
				blockHeader, ok := block.(*common.BlockHeader)
				if !ok {
					log.Fatalln("require true")
				}

				fmt.Printf("recv blockHeader [%d] => \n", blockHeader.BlockHeight)
			} else {
				blockInfo, ok := block.(*common.BlockInfo)
				if !ok {
					log.Fatalln("require true")
				}
				fmt.Printf("recv blockInfo [%d] => \n", blockInfo.Block.Header.BlockHeight)

				// fmt.Printf("recv blockInfo [%d] => %+v\n", blockInfo.Block.Header.BlockHeight, blockInfo)
			}
		case <-ctx.Done():
			return
		}
	}
}

// invokeChainCode 执行存证合约
func invokeChainCode(data interface{}) {
	goPoolData := data.(GoPoolData)
	client := goPoolData.client
	chainCodeData := goPoolData.chainCodeData
	wg := goPoolData.wg
	defer wg.Done()
	kvs := []*common.KeyValuePair{
		{
			Key:   "method",
			Value: []byte("Evidence"),
		},
		{
			Key:   "id",
			Value: []byte(chainCodeData.Id),
		},
		{
			Key:   "hash",
			Value: []byte(chainCodeData.Hash),
		},
		{
			Key:   "metadata",
			Value: []byte(chainCodeData.Metadata),
		},
	}
	startTime := time.Now()
	fmt.Println(kvs)
	err := invokeUserContract(client, claimContractName, "Evidence", "", kvs, true, &common.Limit{GasLimit: 200000})
	endTime := time.Now()
	log.Printf("存证合约执行id:%s,开始时间:%v, 结束时间:%v, 耗时：%v, 错误：%v", chainCodeData.Id, startTime, endTime, endTime.Sub(startTime), err)
	return
}

func queryChainCode(data interface{}) {
	goPoolData := data.(GoPoolData)
	client := goPoolData.client
	chainCodeData := goPoolData.chainCodeData
	wg := goPoolData.wg
	defer wg.Done()
	kvs := []*common.KeyValuePair{
		{
			Key:   "method",
			Value: []byte("FindByHash"),
		},
		{
			Key:   "hash",
			Value: []byte(chainCodeData.Hash),
		},
	}
	startTime := time.Now()
	resp, err := client.QueryContract(claimContractName, "FindByHash", kvs, -1)
	endTime := time.Now()
	log.Printf("查询合约执行hash:%s 结果:%s,开始时间:%v, 结束时间:%v, 耗时：%v, 错误：%v", chainCodeData.Hash, resp.String(), startTime, endTime, endTime.Sub(startTime), err)
	return
}

func invokeUserContract(client *sdk.ChainClient, contractName, method, txId string,
	kvs []*common.KeyValuePair, withSyncResult bool, limit *common.Limit) error {

	fmt.Println(contractName, method, txId, kvs, -1, withSyncResult, limit)
	resp, err := client.InvokeContractWithLimit(contractName, method, txId, kvs, -1, withSyncResult, limit)
	if err != nil {
		return err
	}

	if resp.Code != common.TxStatusCode_SUCCESS {
		return fmt.Errorf("invoke contract failed, [code:%d]/[msg:%s]\n", resp.Code, resp.Message)
	}

	if !withSyncResult {
		fmt.Printf("invoke contract success, resp: [code:%d]/[msg:%s]/[txId:%s]\n", resp.Code, resp.Message, resp.ContractResult.Result)
	} else {
		fmt.Printf("invoke contract success, resp: [code:%d]/[msg:%s]/[contractResult:%s]\n", resp.Code, resp.Message, resp.ContractResult)
	}

	return nil
}
